#include "dsound.h"

void mysound()
{
IDirectSound8 *g_pDS; //global IDirectSound8 object



/* First you must create a pointer to the Direct Sound object, done above
   Then you actually create the object, done below.

HRESULT WINAPI DirectSoundCreate8(
	LPGUID lpGuidDevice,   set to NULL(default sound device
	LPDIRECTSOUND8 *ppDS8, object you are creating
	LPUKNOUTER pUnkOuter); NULL - not used */

if(FAILED(DirectSoundCreate8(NULL, &g_pDS, NULL)))
	{
		//error occurred
	}

/* Once the object is created you must set the cooperative lvl, bascially, are you going to share access to the 
   sound card or hog it, etc.

   IDirectSound8::SetCooperativeLevel (

   HWND hwnd,			handle to parent window
   DWORD dwLevel)		cooperative level which may consist of:

   Level			Macro			Description
   Normal			DSSCL_NORMAL
   Priority			DSSCL_PRIORITY
   Exclusive		DSSCL_EXCLUSIVE
   WritePrimary		DSSCL_WRITEPRIMARY  */

if(FAILED(g_pDS->SetCooperativeLevel(ghwnd, DSSCL_PRIORITY)))
{
	//error occurred
}

/* Next you must create the primary sound buffer, two step process

HRESULT IDirectSound8::CreateSoundBuffer(
	LPCDSBUFFERDESC pcDSBufferDesc,			//description of buffer
	LPDIRECTSOUNDBUFFER *ppDSBuffer,		//buffer object to create
	LPUKNOWN pUnkOuter)						//NULL - not used

put typedef struct here */

IDIRECTSOUNDBUFFER g_pDSPrimary;  //global access

DSBUFFERDESC dsbd; //buffer description

ZeroMemory(&dsbd, sizeof(DSBUFFERDESC)); //zero out structure, basically clear it for use

dsbd.dwSize = sizeof(DSBUFFERDESC);  //set structure size

dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; 
dsbd.dwBufferBytes= 0		//no buffer size
dsbd.lpfxFormat = NULL;		//no format yet
if(FAILED(g_pDS->CreateSoundBuffer(&dsbd, &g_pDSPrimary, NULL)))
{
	//error occurred
}

/* Next we set the format using IDIRECTSOUND8::SetFormat

HRESULT IDirectSoundBuffer::SetFormat(
	LPCWAVEFORMATEX pcfxFormat);

	put note here
	put typedef struct here  
*/

WAVEFORMATEX wfex;

ZeroMemory(&wfex, sizeof(WAVEFORMATEX));

wfex.wFormatTag = WAVE_FORMAT_PCM;
wfex.wChannels = 1;	//mono
wfex.nSamplesPerSec = 22050; //22050hz
wfex.wBitsPerSample = 16; //16-bit
wfex.nBlockAlign = (wfex.wBitsPerSample/8) * wfex.nChannels;
wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
if(FAILED(g_pDSPrimary->SetFormat(&wfex)))
{
	//error occurred
}

/* Now you are ready to start the sound buffer with a call to:
	HRESULT IDirectSoundBuffer8::Play(
		DWORD Reserved1, //must be 0
		DWORD Priority,		//mixing priority, set to 0
		DWORD dwFlags)		// playback flags, 0 to not loop, DSBPLAY_LOOPING to loop
	*/

if(FAILED(g_pDSPrimary->Play(0,0,DSBPLAY_LOOPING))) {
	//error occurred
}

//Now you must create the secondary sound buffers that you will use, right now there is only
//2 seconds of storage since we have not loaded any sounds

//g_pDS = pre-initialized IDirectSound8 object
IDirectSoundBuffer8 *g_pDSBuffer; //version 8 global object we want
IDirectSoundBuffer *pDSB;		  //local sound buffer

//WAVEFORMATEX wfex; only use if you haven't declared

ZeroMemory(&wfex, sizeof(WAVEFORMATEX));

wfex.wFormatTag = WAVE_FORMAT_PCM;
wfex.wChannels = 1;	//mono
wfex.nSamplesPerSec = 22050; //22050hz
wfex.wBitsPerSample = 16; //16-bit
wfex.nBlockAlign = (wfex.wBitsPerSample/8) * wfex.nChannels;
wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;

//Set up the DSBUFFERDESC structure
//DSBUFFERDESC dsbd; //buffer description, only needs to be declared if you haven't already done so

ZeroMemory(&dsbd, sizeof(DSBUFFERDESC)); //zero out structure, basically clear it for use

dsbd.dwSize = sizeof(DSBUFFERDESC);  //set structure size

dsbd.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN; 
dsbd.dwBufferBytes= wfex.nAvgBytesPerSec * 2;	//2 seconds
dsbd.lpfxFormat = &wfex;		

//Create First Version Object
if(FAILED(g_pDS->CreateSoundBuffer(&dsbd, &pDSB, NULL)))
{
	//error occurred
}
else
{
	//Get the version 8 interface
	if(FAILED(pDSB->QueryInterface(IID_IDirectSoundBuffer8, (void**)&g_pDSBuffer)))
	{
		//Error occurred - free first interface first and then do something
		pDSB->Release();
	}
	else
	{
		//release the original interface - all a success
		pDSB->Release();
	}
}

/* lock and load time

HRESULT IDirectSoundBuffer8::Lock(
	DWORD dwOffset,					//offset in buffer where to lock
	DWORD dwBytes,					//# of bytes to lock
	LPVOID *ppvAudioPtr1,			//pointer to 1st data pointer
	LPDWORD *ppwAudioBytes1,		//pointer to 1st size
	LPVOID *ppvAudioPtr2,			//pointer to 2nd data pointer
	LPDWORD *ppwAudioBytes2,		//pointer to 2nd size
	DWORD dwFlags);					//locking flags
*/

//g_pDSBuffer = preinitialized secondary sound buffer
char *Ptr;
DWORD Size;
if(SUCCEEDED(g_pDSBuffer->Lock(0,0,(void**)&Ptr,(DWORD*)&Size, NULL, 0, DSBLOCK_ENTIREBUFFER)))
{
	for(long i=0; i<Size;i++)
		Ptr[i] = rand() % 65536;
	
	//done with buffer, unlock it
	
	/* HRESULT IDirectSoundBUffer8::Unlock(
		LPVOID pvAudioPtr1, 
		DWORD dwAudioBytes1,
		LPVOID pvAudioPtr2,
		DWORD dwAudioBytes2);
	*/

	if(FAILED(g_pDSBuffer->Unlock((void*)Ptr, Size, NULL, 0)))
	{
		//Error Occurred
	}
}

//causes buffer to play at beginning each time
if(SUCCEEDED(g_pDSBuffer->SetCurrentPosition(0)))
{
	if(FAILED(g_pDSBuffer->Play(0,0,0)))
	{
		//error occurred
	}
}

//to stop playback
if(FAILED(g_pDSBuffer->Stop()))
{
	//Error Occurred
}

//to adjust volume must include DSBCAPS_CTRLVOLUME when creating buffer
//can adjust buffer from 0 to -10000(silence) decibels, also can use
//macros, which are DSBVOLUME_MAX & DSBVOLUME_MIN
//HRESULT IDirectSoundBuffer8::SetVolume(LONG lVolume);
/* ex: if(FAILED(g_pDSBuffer->SetVolume(-2500)))
{
	error occurred
} //adjust volume by 25 decibels

/* PANNING
left only = -10000 or DSBPAN_LEFT  &  right only = 10000 or DSBPAN_RIGHT
need to use DSBCAPS_CTRLPAN flag when creating the sound buffer to use this feature
also make sure the primary buffer supports a 16 bit buffer or the pan effect might not sound right
HRESULT IDirectSoundBuffer8::SetPan(LONG lPan);
ex:
//g_pDSBuffer = preinitialized sound buffer
if(FAILED(g_pDSBuffer->SetPan(-5000)))
{
	//error occurred
}
*/

/* Frequency
must include the flag DSBCAPS_CTRLFREQUENCY when creating the sound buffer
HRESULT IDirectSoundBuffer8::SetFrequency(DWORD dwFrequency);

ex:
if(FAILED(g_pDSBuffer->SetFrequency(22050)))
{
	//error occurred
}

Losing Focus
happens when another application grabs resources away from your game, leaving you with a device
that has been altered, if this happens you can restore the buffer but the sound must be reloaded
TIP: when creating the sound buffer use DSBCAPS_LOCSOFTWARE to tell DirectSound to use system memory
resources which are rarely lost and you will not have to worry about this


//when you are done with the sound buffer be sure to stop it
if(FAILED(g_pDSPrimary->Stop())) {
	//error occurred
}*/
}

typedef struct sWaveHeader {
	char RiffSig[4];		//'RIFF'
	long WaveformChunkSize;	//8
	char WaveSig[4];		//'WAVE'
	char FormatSig[4]		//'fmt ' notice space
	long FormatChunkSize;	//16
	short FormatTag;		//WAVE_FORMAT_PCM
	short Channels;			//# of channels
	long SampleRate;		//sampling rate
	long BytesPerSec;		//bytes per sec
	short BlockAlign;		//sample block allignment
	short BitsPerSample;	//bits per second
	char DataSig[4];		//'data'
	long DataSize;			//size of waveform data
}sWaveHeader;

//g_pDS = preinitialized IDirectSound8 object
IDirectSound8 *CreateBufferFromWAV(FILE *fp, sWaveHeader *Hdr)
{
	IDirectSoundBuffer *pDSB;
	IDirectSoundBuffer8 *pDSBuffer;
	DSBUFFERDESC dsbd;
	WAVEFORMATEX wfex;

	//read in the header from the beginning of file
	fseek(fp, 0, SEEK_SET);
	fread(Hdr, 1, sizeof(sWaveHeader), fp);

	//check the sig fields, returning if an error
	if(memcmp(Hdr->RiffSig, "RIFF", 4) || 
		memcmp(Hdr->WaveSig, "WAVE, 4) ||
		memcmp(Hdr->FormatSig, "fmt ", 4) ||
		memcmp(Hdr->DataSig, "data", 4))
		return NULL;

	//set up the playback format
	ZeroMemory(&wfex, sizeof(WAVEFORMATEX));

	wfex.wFormatTag = WAVE_FORMAT_PCM;
	wfex.wChannels = Hdr->Channels;
	wfex.nSamplesPerSec = Hdr->SampleRate;
	wfex.wBitsPerSample = Hdr->BitsPerSample;
	wfex.nBlockAlign = wfex.wBitsPerSample/8 * wfex.nChannels;
	wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;

	//create the sound buffer using the header data
	ZeroMemory(&dsbd, sizeof(DSBUFFERDESC)); //zero out structure, basically clear it for use

	dsbd.dwSize = sizeof(DSBUFFERDESC);  //set structure size

	dsbd.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN; 
	dsbd.dwBufferBytes= Hdr->DataSize;
	dsbd.lpfxFormat = &wfex;		

	//Create First Version Object
	if(FAILED(g_pDS->CreateSoundBuffer(&dsbd, &pDSB, NULL)))
		return NULL;
	
	//Get the version 8 interface
	if(FAILED(pDSB->QueryInterface(IID_IDirectSoundBuffer8, (void**)&g_pDSBuffer)))
	{
		//Error occurred - free first interface first and then do something
		pDSB->Release();
		return NULL:
	}
	
	//return the Interface
	return pDSBuffer;
}

BOOL LoadSoundData(IDirectSoundBuffer8 *pDSBuffer, long LockPos, FILE *fp, long Size)
{
	BYTE *Ptr1, *Ptr2;
	DWORD Size1, Size2;

	if(!Size)
		return FALSE;

	//lock the sound buffer at position specified
	if(FAILED(pDSBuffer->Lock(LockPos, Size, (void**)&Ptr1, &Size1, (void**)&Ptr2, &Size2, 0)))
		return FALSE;

	//read the data
	fread(Ptr1, 1, Size1, fp);
	if(Ptr2 != NULL)
		fread(Ptr2, 1, Size2, fp);

	//unlock it
	pDSBuffer->Unlick(Ptr1, Size1, Ptr2, Size2);

	//return a success
	return TRUE;
}

IDirectSoundBuffer8 *LoadWAV(char *Filename)
{
	IDirectSoundBuffer8 *pDSBuffer;
	sWaveHeader Hdr;
	FILE *fp;

	//open the source file
	if((fp==fopen(Filename, "rb"))==NULL)
		return NULL;

	//create the sound buffer
	if((pDSBuffer = CreateBufferFromWAV(fp, &Hdr)) == NULL)
	{
		fclose(fp);
		return NULL;
	}

	//read in the data
	fseek(fp, sizeof(sWaveHeader), SEEK_SET);
	LoadSoundData(pDSBuffer, 0, fp, Hdr.DataSize);

	//close the source file
	fclose(fp);

	//return the new sound buffer fully loaded with sound
	return pDSBuffer;
}